package com.example.signdemo.services.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.core.libs.AppException;
import com.example.signdemo.entities.UserSignL;
import com.example.signdemo.mapper.UserSignLMapper;
import com.example.signdemo.services.IUserSignLService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Iterator;
import java.util.List;

@Service
@Transactional(rollbackFor = Exception.class)
public class UserSignLImpl extends ServiceImpl<UserSignLMapper, UserSignL> implements IUserSignLService {
    @Autowired
    UserSignLMapper userSignLMapper;

    /**
     * 签到
     *
     * @param userId
     */
    @Override
    public void sign(long userId) {
        LocalDate date = LocalDate.now();
        if (findOneByUserIdAndSingDate(userId, date) != null) {
            throw new AppException("该时间已经签到");
        }

        UserSignL sign = new UserSignL();
        sign.setUserId(userId);
        sign.setSignDate(date);
        sign.setSignCount(1);

        this.save(sign);
        this.updateSingCount(userId, date);
    }

    /**
     * 补签
     *
     * @param userId
     * @param day
     */
    @Override
    public void againSign(long userId, String day) {
        LocalDate date;
        try {
            date = LocalDate.parse(buildYmd(LocalDate.now(), day), DateTimeFormatter.ISO_LOCAL_DATE);
        } catch (Exception e) {
            e.printStackTrace();
            throw new AppException("时间转换错误");
        }

        if (date.getDayOfMonth() >= LocalDate.now().getDayOfMonth()) {
            throw new AppException("补签日期错误");
        }

        if (findOneByUserIdAndSingDate(userId, date) != null) {
            throw new AppException("该时间已经签到");
        }

        UserSignL sign = new UserSignL();
        sign.setUserId(userId);
        sign.setSignDate(date);
        sign.setType(UserSignL.AGAIN_SIGN);
        sign.setSignCount(1);// 如果是1号则需要写入签到次数1

        this.save(sign);
        this.updateSingCount(userId, date);
    }

    @Override
    public int signDays(long userId) {
        return findCountByThisMonth(userId);
    }

    /**
     * 构建指定日的日期
     *
     * @param date
     * @param day
     * @return
     */
    private String buildYmd(LocalDate date, String day) {
        return String.format("%s-%s-%s", date.getYear(), date.getMonthValue(), day);
    }

    private String buildYm(LocalDate date) {
        return String.format("%s-%s", date.getYear(), date.getMonthValue());
    }

    /**
     * 更新签到次数
     *
     * @param userId
     * @param date
     */
    private void updateSingCount(long userId, LocalDate date) {
        List<UserSignL> userSignLList;
        if (date.getDayOfMonth() == 1) {
            userSignLList = findByUserIdAndLgSingDate(userId, date);
        } else {
            // 找出补签前一天到现在的记录
            userSignLList = findByUserIdAndLgSingDate(userId, date.plusDays(-1));
        }

        if (!userSignLList.isEmpty()) {
            Iterator<UserSignL> iterator = userSignLList.iterator();
            UserSignL flag = iterator.next();
            while (iterator.hasNext()) {
                UserSignL next = iterator.next();
                // 如果没有漏签，则签到次数++
                if (flag.getSignDate().until(next.getSignDate(), ChronoUnit.DAYS) == 1) {
                    next.setSignCount(flag.getSignCount() + 1);
                    flag = next;
                } else {
                    break;
                }
            }
            this.updateBatchById(userSignLList);
        }
    }

    private UserSignL findOneByUserIdAndSingDate(long userId, LocalDate localDate) {
        LambdaQueryWrapper<UserSignL> lambda = new LambdaQueryWrapper<>();
        lambda.eq(UserSignL::getUserId, userId).eq(UserSignL::getSignDate, localDate);
        return userSignLMapper.selectOne(lambda);
    }

    private List<UserSignL> findByUserIdAndLgSingDate(long userId, LocalDate localDate) {
        LambdaQueryWrapper<UserSignL> lambda = new LambdaQueryWrapper<>();
        lambda.eq(UserSignL::getUserId, userId).ge(UserSignL::getSignDate, localDate).orderByAsc(UserSignL::getSignDate);
        return userSignLMapper.selectList(lambda);
    }

    private int findCountByThisMonth(long userId) {
        LambdaQueryWrapper<UserSignL> lambda = new LambdaQueryWrapper<>();
        lambda.likeRight(UserSignL::getSignDate, buildYm(LocalDate.now())).eq(UserSignL::getUserId, userId);
        return userSignLMapper.selectCount(lambda);
    }
}
